// me_samsung.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "AviMovie.h"
#include "ME.h"
#include <vector>

using namespace std;

#define Y 0
#define U 1
#define V 2


void getDomain(byte *pFrame, 
				byte *pDomain, 
				unsigned uXPos, 
				unsigned uYPos, 
				unsigned uDomainWidth, 
				unsigned uDomainHeight,
				unsigned uFrameWidth, 
				unsigned uFrameHeight){
	for (unsigned i = 0 ; i < uDomainHeight;i++)
		memcpy(&pDomain[i * uDomainWidth], &pFrame[uFrameWidth * (uYPos + i) + uXPos],uDomainWidth);	 
}

void setDomain(byte *pFrame, 
				byte *pDomain, 
				unsigned uXPos, 
				unsigned uYPos, 
				unsigned uDomainWidth, 
				unsigned uDomainHeight,
				unsigned uFrameWidth, 
				unsigned uFrameHeight){
	for (unsigned i = 0 ; i < uDomainHeight;i++)
		memcpy(&pFrame[uFrameWidth * (uYPos + i) + uXPos],&pDomain[i * uDomainWidth], uDomainWidth);
}

int _tmain(int argc, _TCHAR* argv[]){
	bool isFullSearch;
	unsigned uSearchRadius, uDomainWidth, uDomainHeight;
	if(argc<6){
		printf("USAGE: me_samsung.exe <filename> <algoritm> <initial_search_radius> <domain_width> <domain_height>\n");
		printf("<algorithm> : full|3ss\n");
		exit(0);
	}
	if(strcmp(argv[2], "full") == 0)
		isFullSearch = true;
	else if(strcmp(argv[2], "3ss") == 0)
		isFullSearch = false;
	else{
		printf("Incorrect algorithm name");
		exit(-1);
	}
	uSearchRadius = atoi(argv[3]);
	uDomainWidth = atoi(argv[4]);
	uDomainHeight = atoi(argv[5]);

	if(uDomainWidth == 0 || uDomainHeight == 0 || uSearchRadius == 0){
		printf("Incorrect parameter(s)");
		exit(-1);
	}

	AviMovie * movieIn = new AviMovie("claire80.avi");

	unsigned uPlaneSize = movieIn->getPlaneSize();
	unsigned uFrameWidth = movieIn->getWidth();
	unsigned uFrameHeight = movieIn->getHeight();

	if(uFrameWidth%uDomainWidth !=0 || uFrameHeight%uDomainHeight != 0){
		printf("Frame size should match domain size");
		exit(-1);
	}

	unsigned uDomainsPerWidth = uFrameWidth/uDomainWidth;
	unsigned uDomainsPerHeight = uFrameHeight/uDomainHeight;

	AviMovie * movieOut = new AviMovie("out.avi",movieIn->getWidth(),movieIn->getHeight(),movieIn->getScale(),movieIn->getRate(),movieIn->getLength());
	ME * instME = new ME();
//	typedef int(ME::*MEAlg)(byte *, byte *, int, int, int &, int &, int, int, int, int, int);
//	MEAlg pMEalgorithm;
//	if(isFullSearch)
//		pMEalgorithm = &ME::doDomainMotionEstimationFull;
//	else
//		pMEalgorithm = &ME::doDomainMotionEstimation3SS;

	// declare 3 storage (for Base, Current and Restored frames), each contains 3 bitplanes (Y, U and V)
	vector<vector<vector<byte>>> pFrames(3,vector<vector<byte>>(3, vector<byte>(uPlaneSize)));

	// declare [uDomainsPerHeight x uDomainsPerWidth] matrix of MC vectors
	//vector<vector<pair<int, int>>> MCvectors(uDomainsPerHeight,vector<pair<int, int>>(uDomainsPerWidth));

	//vector <byte> pValue127 (uPlaneSize, 127);
	vector <byte> pDomainHolder(uDomainHeight*uDomainWidth);

	unsigned currFrameInd = 1, baseFrameInd = 0;
	unsigned i,j,k,xPos,yPos;
	int dx,dy;
	unsigned diff;

	movieIn->getNextFrameYUV(&pFrames[0][Y][0], &pFrames[0][U][0], &pFrames[0][V][0]);
	//copy first frame to output sequence
	movieOut->putNextFrameYUV(&pFrames[0][Y][0], &pFrames[0][U][0], &pFrames[0][V][0]);
	for(i = 1; i<movieIn->getLength(); i++){
		movieIn->getNextFrameYUV(&pFrames[currFrameInd][Y][0], &pFrames[currFrameInd][U][0], &pFrames[currFrameInd][V][0]);
		for(j = 0; j<uDomainsPerHeight; j++){
			yPos = j*uDomainHeight;
			for(k = 0; k<uDomainsPerWidth; k++){
				//compenstate only Y plane
				xPos = k*uDomainWidth;
				getDomain(&pFrames[currFrameInd][Y][0],&pDomainHolder[0],xPos,yPos,uDomainWidth,uDomainHeight,uFrameWidth,uFrameHeight);
				if(isFullSearch){
					instME->doDomainMotionEstimationFull(&pFrames[baseFrameInd][Y][0],&pDomainHolder[0],xPos,yPos,
							dx,dy/*(MCvectors[j][k].first),MCvectors[j][k].second*/,uSearchRadius, uDomainWidth,uDomainHeight,uFrameWidth, uFrameHeight);
				}
				else{
					instME->doDomainMotionEstimation3SS(&pFrames[baseFrameInd][Y][0],&pDomainHolder[0],xPos,yPos,
							dx,dy/*(MCvectors[j][k].first),MCvectors[j][k].second*/,uSearchRadius, uDomainWidth,uDomainHeight,uFrameWidth, uFrameHeight);
				}
				//restoring Y
				getDomain(&pFrames[baseFrameInd][Y][0],&pDomainHolder[0],xPos+dx,yPos+dy,uDomainWidth,uDomainHeight,uFrameWidth,uFrameHeight);
				setDomain(&pFrames[2][Y][0],&pDomainHolder[0],xPos,yPos,uDomainWidth,uDomainHeight,uFrameWidth,uFrameHeight);
				//restoring U and V using MC vectors from Y
				getDomain(&pFrames[baseFrameInd][U][0],&pDomainHolder[0],xPos+dx,yPos+dy,uDomainWidth,uDomainHeight,uFrameWidth,uFrameHeight);
				setDomain(&pFrames[2][U][0],&pDomainHolder[0],xPos,yPos,uDomainWidth,uDomainHeight,uFrameWidth,uFrameHeight);

				getDomain(&pFrames[baseFrameInd][V][0],&pDomainHolder[0],xPos+dx,yPos+dy,uDomainWidth,uDomainHeight,uFrameWidth,uFrameHeight);
				setDomain(&pFrames[2][V][0],&pDomainHolder[0],xPos,yPos,uDomainWidth,uDomainHeight,uFrameWidth,uFrameHeight);
			}
		}
		movieOut->putNextFrameYUV(&pFrames[2][Y][0], &pFrames[2][U][0], &pFrames[2][V][0]);
		//flip indexes
		currFrameInd = 1 - currFrameInd;
		baseFrameInd = 1 - baseFrameInd;
	}

	delete movieIn;
	delete movieOut;
	delete instME;
	return 0;
	
}

